package com.itzg.svn;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tigris.subversion.javahl.ClientException;
import org.tigris.subversion.javahl.Depth;
import org.tigris.subversion.javahl.SVNClient;
import org.tigris.subversion.javahl.SVNClientInterface;

import com.itzg.fileutil.DirTreeFiller;
import com.itzg.fileutil.FillerProgressListener;
import com.itzg.fileutil.ScenarioFactory;

public abstract class SvnSlammer {
	private static final Logger logger = LoggerFactory
			.getLogger(SvnSlammer.class);
	
	private File scenarioFile;
	
	private File workareaDir;

	private SVNClientInterface svnClient;
	
	private SvnTimings timings = new SvnTimings();

	private File trunkWorking;

	private final SvnSlammerOptions options;

	public SvnSlammer(SvnSlammerOptions options) {
		this.options = options;
		scenarioFile = options.getScenarioFile();
		workareaDir = options.getWorkareaDir();
		if (scenarioFile == null || workareaDir == null) {
			throw new IllegalStateException("Missing scenario file or workarea directory config");
		}
	}

	public void run() {
		setupRepo();
		
		svnClient = new SVNClient();
		
		try {
			createAndCheckinTrunk();
		} catch (IOException e) {
			logger.error("Failed to perform initial commit", e);
			return;
		} catch (ClientException e) {
			logger.error("Failed to perform initial commit", e);
			return;
		}
		
		// Now we're good to go
		
		Trunk trunk = new Trunk(trunkWorking);
		
		// and print out stats
		
		logger.info("Trunk contained {} files for a total of {} bytes", trunk.getFileCount(), trunk.getTotalFileSize());
		for (SvnOpRecording svnOpRecording : timings.getRecordings()) {
			logger.info("Recording at {}", svnOpRecording);
		}
	}

	private void createAndCheckinTrunk() throws IOException, ClientException {
		workareaDir.mkdirs();
		trunkWorking = new File(workareaDir, "trunk");
		
		populateWorkArea();
		
		final String trunkModule = getRepoBaseUrl()+"trunk";
		svnClient.mkdir(new String[]{trunkModule}, "");
		svnClient.checkout(trunkModule, trunkWorking.getAbsolutePath(), null, null, Depth.immediates, true, true);
		File[] workingFiles = trunkWorking.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return !name.equals(".svn");
			}
		});
		for (File file : workingFiles) {
			svnClient.add(file.getAbsolutePath(), Depth.infinity, true, false, false);
		}
		commit(trunkWorking, null);
	}

	private void populateWorkArea() throws IOException {
		DirTreeFiller filler = new DirTreeFiller(trunkWorking, ScenarioFactory.loadFromFile(scenarioFile));
		FillerProgressListener fillerListener = new FillerProgressListener() {
			@Override
			public void updatedProgress(float filledByElements,
					float filledBySize) {
				float biggerOne = filledByElements > filledBySize ? filledByElements : filledBySize;
				logger.info("{}%...", (int)(biggerOne*100));
			}
		};
		filler.setProgressListener(fillerListener);
		logger.info("Filling work area...");
		filler.fill(true);
		logger.info("Filling done");
	}

	private long commit(File workingDir, String message) throws ClientException {
		long result;
		long start = System.nanoTime();
		result = svnClient.commit(new String[]{workingDir.getAbsolutePath()}, message != null ? message : "", Depth.infinity, false, false, null, null);
		timings.record(new SvnOpRecording(SvnOp.COMMIT, SvnPlace.TRUNK, start, System.nanoTime(), result, message));
		return result;
	}

	public void setupRepo() {
		// nothing in base impl
	}
	
	protected abstract String getRepoBaseUrl();
}
